// find.c
// By jjgod for FYTX.

#include <ansi.h>

// ENABLE_BAR 模式针对类 hell 的 MUD，它提供了 process_bar()
// 这个 simul_efun 和 attack_system(),detach_system() 这两个
// 定义在 USER_OB 的函数，并且利用 ANSI 控制符对提示符有了一
// 些改进。开启时定义为 1 即可。
#define ENABLE_BAR      1

inherit F_CLEAN_UP;

void create() { seteuid(getuid()); }

public void search_dir(object me, string file, string dir, int raw);
public void search_in_file(object me, string info, string file);
public string *search_file(string file, string *flist, object me, int raw);
public string *deep_file_list(string dir);
string itoa(int i) { return sprintf("%d", i); }

int main(object me, string arg)
{
        string file, dir, flag;
        int raw;

        // 权限检查，需要安全系统支持
        if (! SECURITY_D->valid_grant(me, "(arch)"))
                return 0;

        if (! arg)
                return notify_fail("指令格式：find <文件名|内容> in <目录名|文件名> [-c]\n");

        if (sscanf(arg, "%s in %s %s", file, dir, flag) != 3)
        {
                if (sscanf(arg, "%s in %s", file, dir) != 2)
                        notify_fail("指令格式：find <文件名|内容> in <目录名|文件名> [-c]\n");
        }

        // 如果是查找包含内容模式
        if (flag == "-c") raw = 1;

        dir=resolve_path(query("cwd", me),dir);

        seteuid(getuid(me));

        // 如果是在文件中查找模式
        if (file_size(dir) >= 0)
        {
                search_in_file(me, file, dir);
                return 1;
        }

        if (file_size(dir) == -2 && dir[strlen(dir) - 1] != '/') dir += "/";
        if (file_size(dir) != -2)
                return notify_fail(dir + " 并不是一个目录。\n");

        // 给一点提示，因为玩家可能会过于迟滞
        message_system("系统进行数据处理中，请耐心等候...");

#if ENABLE_BAR
        write(HIR "\n现在系统正在搜索 " + dir + " 目录，稍后汇报。\n\n" NOR
              HIW "进度：" + process_bar(0) + "\n");
        if (me)
        {
                me->attach_system();
                me->write_prompt();
        }
#endif
        search_dir(me, file, dir, raw);
        return 1;
}

void search_dir(object me, string file, string dir, int raw)
{
        int i;
        string *flist, *result;
        string info, file_info, size;

        // 获得所有的深层目录文件列表
        flist = deep_file_list(dir);

        if (! arrayp(flist) || ! sizeof(flist))
        {
                message_system("系统数据处理完毕，请继续游戏。\n" ESC + "[K");
                info = HIR "文件搜索完毕：\n\n" NOR ESC + "[K"
                       HIR "目录 " + dir + " 下并没有可供查找的文件。" NOR;

                message("system", info, me);
#if ENABLE_BAR
                me->detach_system();
#endif
                return;
        }

        result = search_file(file, flist, me, raw);

#if ENABLE_BAR
        me->detach_system();
#endif

        message_system("系统数据处理完毕，请继续游戏。\n" ESC + "[K");

        if (! sizeof(result))
                info = HIR "文件搜索完毕：\n" + ESC + "[K" + "\n" NOR ESC + "[K"
                       HIR "目录 " + dir + " 下没有找到任何符合要求的文件。\n" NOR
                       ESC + "[K";

        else
        {
                file_info = "";
                for (i = 0; i < sizeof(result); i++)
                {
                        if (eval_cost() < 10000) set_eval_limit(0);
                        size = itoa(file_size(result[i]) / 1000);
                        file_info += sprintf(CYN "%-45s " WHT "%4s " CYN " %s\n",
                                             result[i],
                                             (size == "0" ? itoa(file_size(result[i])) + "b" : size + "k"),
                                             CHINESE_D->chinese_time(9, ctime(stat(result[i])[1])), );
                }

                info = HIR "文件搜索完毕：\n" + ESC + "[K" + "\n" NOR ESC + "[K" +
                       file_info + NOR + ESC + "[K"
                       HIR "\n一共找到 " + sizeof(result) + " 个文件。" + ESC + "[K" + NOR
                       ESC + "[K\n" + ESC + "[K";
        }

        if (me)
                me->start_more(info);
//              message("system", info, me);
}

string *deep_file_list(string dir)
{
        int i;
        string *flist, *result = ({ });
        string file;

        flist = get_dir(dir);

        for (i = 0; i < sizeof(flist); i++)
        {
                if (eval_cost() < 10000) set_eval_limit(0);
                file = dir + flist[i];

                if (file_size(file + "/") == -2)
                        result += deep_file_list(file + "/");
                else
                        result += ({ file });
        }

        return result;
}

string *search_file(string file, string *flist, object me, int raw)
{
        int i, j;
        string *result = ({ });
        string file_info;

        for (i = 0; i < sizeof(flist); i++)
        {
                if (eval_cost() < 10000) set_eval_limit(0);
#if ENABLE_BAR
                message("system", NOR ESC + "[1A" + ESC + "[256D"
                                  HIG "进度：" + process_bar((i + 1) * 100 / sizeof(flist)) +
                                  "\n" + ESC + "[K", me);
#endif
                if (raw == 0)
                {
                        j = strsrch(flist[i], "/", -1);
                        file_info = flist[i][j..(sizeof(flist[i]) - 1)];

                        if (file_info == file)
                        {
                                result += ({ flist[i] });
                                continue;
                        }
                        if (strsrch(file_info, file) > -1)
                        {
                                result += ({ flist[i] });
                                continue;
                        }
                }
                else
                {
                        // 如果文件本身都没有这个要找的字符串长
                        if (file_size(flist[i]) < strlen(file))
                                continue;

                        if (catch(read_file(flist[i])))
                                log_file("readfile", sprintf("%s\n", flist[i]));
                        if (! read_file(flist[i]))
                                continue;

                        if (strsrch(read_file(flist[i]), file) > -1)
                        {
                                result += ({ flist[i] });
                                continue;
                        }
                }
        }
        return result;
}

void search_in_file(object me, string word, string file)
{
        string text, info;
        string *lines;
        int line, num, i, count = 0;

        seteuid(getuid(me));
        if (catch(read_file(file)))
                log_file("readfile", sprintf("%s\n", file));
        if (! text = read_file(file))
        {
                write(HIR "\n文件 " + file + " 打开失败，可能是因为文件过大。\n" NOR);
                return;
        }

        if (strsrch(text, word) == -1)
        {
                write(HIR "\n文件 " + file + " 内部并无包含“" HIW + word +
                      HIR "”字符串。\n" NOR);
                return;
        }

        lines = explode(text, "\n");
        info = HIR "正在文件 " + file + " 中查找包含 " + word + "\n字符串的内容：\n\n" NOR;

        line = sizeof(lines);
        for (i = 0; i < line; i++)
        {
                if (eval_cost() < 10000) set_eval_limit(0);
                num = strsrch(lines[i], word);

                if (num > -1)
                {
                        info += WHT "在第 " + (i + 1) + " 行的第 " + (num + 1) + " 字节处"
                                "找到指定字符。\n" NOR;

                        info += CYN "............\n" NOR;
                        info += (i - 1) >= 0 ? CYN + lines[i - 1] + "\n" + NOR : "";
                        info += CYN + replace_string(lines[i], word, WHT + word + CYN) + "\n" + NOR;
                        info += (i + 1) < line ? CYN + lines[i + 1] + "\n" + NOR : "";
                        info += CYN "............\n\n" NOR;

                        count++;
                }
        }
        info += HIR "查询完毕，一共找到 " + count + " 个符合的内容。" NOR;

        me->start_more(info);
        return;
}

int help(object me)
{
        write(@HELP
指令格式: find <文件名|内容> in <目录名|文件名> [-c]

查找目录及其子目录下所有包含指定文件名的文件和目录。查找结果返
回的格式为：<文件名> <文件大小> <上一次修改时间>。
如果加上了 -c 参数则表示查找在指定目录下包含指定内容的文件。
如果指定的位置是一个文件名，则表示在那个文件中查找包含指定内容
的行。
HELP );
        return 1;
}